home *** CD-ROM | disk | FTP | other *** search
/ HAM Radio 3.2 / Ham Radio Version 3.2 (Chestnut CD-ROMs)(1993).ISO / packet / n17jsrc / kernel.c < prev    next >
C/C++ Source or Header  |  1991-04-08  |  13KB  |  529 lines

  1. /* Non pre-empting synchronization kernel, machine-independent portion
  2.  * Copyright 1991 Phil Karn, KA9Q
  3.  */
  4.  
  5. #define    SUSPEND_PROC    1
  6. #undef    PROCTRACE    1    /* kernel debugging */
  7. #undef    PROCLOG        1    /* debugging */
  8.  
  9. #if    defined(PROCLOG) || defined(PROCTRACE)
  10. #include <stdio.h>
  11. #endif
  12. #include <dos.h>
  13. #include <setjmp.h>
  14. #include "global.h"
  15. #include "mbuf.h"
  16. #include "proc.h"
  17. #include "timer.h"
  18. #include "socket.h"
  19. #include "daemon.h"
  20. #include "hardware.h"
  21.  
  22. #ifdef    PROCLOG
  23. FILE *proclog;
  24. FILE *proctrace;
  25. #endif
  26. int Stkchk = 1;
  27. struct proc *Curproc;        /* Currently running process */
  28. struct proc *Rdytab;        /* Processes ready to run (not including curproc) */
  29. struct proc *Waittab[PHASH];    /* Waiting process list */
  30. struct proc *Susptab;        /* Suspended processes */
  31. static struct mbuf *Killq;
  32.  
  33. static void addproc __ARGS((struct proc *entry));
  34. static void delproc __ARGS((struct proc *entry));
  35.  
  36. /* Create a process descriptor for the main function. Must be actually
  37.  * called from the main function!
  38.  * Note that standard I/O is NOT set up here.
  39.  */
  40. struct proc *
  41. mainproc(name)
  42. char *name;
  43. {
  44.     register struct proc *pp;
  45.  
  46.     /* Create process descriptor */
  47.     pp = (struct proc *)callocw(1,sizeof(struct proc));
  48.  
  49.     /* Create name */
  50.     pp->name = strdup(name);
  51. #ifndef    AMIGA
  52.     pp->stksize = 0;
  53. #else
  54.     init_psetup(pp);
  55. #endif
  56.     /* Make current */
  57.     pp->state = READY;
  58.     Curproc = pp;
  59.  
  60. #ifdef    PROCLOG
  61.     proclog = fopen("proclog",APPEND_TEXT);
  62.     proctrace = fopen("proctrace",APPEND_TEXT);
  63. #endif
  64.     return pp;
  65. }
  66. /* Create a new, ready process and return pointer to descriptor.
  67.  * The general registers are not initialized, but optional args are pushed
  68.  * on the stack so they can be seen by a C function.
  69.  */
  70. struct proc *
  71. newproc(name,stksize,pc,iarg,parg1,parg2,freeargs)
  72. char *name;        /* Arbitrary user-assigned name string */
  73. unsigned int stksize;    /* Stack size in words to allocate */
  74. void (*pc)();        /* Initial execution address */
  75. int iarg;        /* Integer argument (argc) */
  76. void *parg1;        /* Generic pointer argument #1 (argv) */
  77. void *parg2;        /* Generic pointer argument #2 (session ptr) */
  78. int freeargs;        /* If set, free arg list on parg1 at termination */
  79. {
  80.     register struct proc *pp;
  81.     int i;
  82.  
  83.     if(Stkchk)
  84.         chkstk();
  85.  
  86.     /* Create process descriptor */
  87.     pp = (struct proc *)callocw(1,sizeof(struct proc));
  88.  
  89.     /* Create name */
  90.     pp->name = strdup(name);
  91.  
  92.     /* Allocate stack */
  93. #ifdef    AMIGA
  94.     stksize += 2000;    /* DOS overhead */
  95. #endif
  96.     pp->stksize = stksize;
  97.     if((pp->stack = (int16 *)malloc(sizeof(int16)*stksize)) == NULL){
  98.         free(pp->name);
  99.         free((char *)pp);
  100.         return NULLPROC;
  101.     }
  102.     /* Initialize stack for high-water check */
  103.     for(i=0;i<stksize;i++)
  104.         pp->stack[i] = STACKPAT;
  105.  
  106.     /* Do machine-dependent initialization of stack */
  107.     psetup(pp,iarg,parg1,parg2,pc);
  108.  
  109.     pp->freeargs = freeargs;
  110.     pp->iarg = iarg;
  111.     pp->parg1 = parg1;
  112.     pp->parg2 = parg2;
  113.     
  114.     /* Inherit creator's input and output sockets */
  115.     usesock(Curproc->input);
  116.     pp->input = Curproc->input;
  117.     usesock(Curproc->output);
  118.     pp->output = Curproc->output;
  119.  
  120.     /* Add to ready process table */
  121.     pp->state = READY;
  122.     addproc(pp);
  123.     return pp;
  124. }
  125.  
  126. /* Free resources allocated to specified process. If a process wants to kill
  127.  * itself, the reaper is called to do the dirty work. This avoids some
  128.  * messy situations that would otherwise occur, like freeing your own stack.
  129.  */
  130. void
  131. killproc(pp)
  132. register struct proc *pp;
  133. {
  134.     char **argv;
  135.  
  136.     if(pp == NULLPROC)
  137.         return;
  138.     /* Don't check the stack here! Will cause infinite recursion if
  139.      * called from a stack error
  140.      */
  141.  
  142.     if(pp == Curproc)
  143.         killself();    /* Doesn't return */
  144.  
  145.     /* Close any open sockets */
  146.     freesock(pp);
  147.  
  148.     close_s(pp->input);
  149.     close_s(pp->output);
  150.  
  151.     /* Stop alarm clock in case it's running */
  152.     stop_timer(&pp->alarm);
  153.  
  154.     /* Alert everyone waiting for this proc to die */
  155.     psignal(pp,0);
  156.  
  157.     /* Remove from appropriate table */
  158.     delproc(pp);
  159.  
  160. #ifdef    PROCLOG
  161.     fprintf(proclog,"id %lx name %s stack %u/%u\n",ptol(pp),
  162.         pp->name,stkutil(pp),pp->stksize);
  163.     fclose(proclog);
  164.     proclog = fopen("proclog",APPEND_TEXT);
  165.     proctrace = fopen("proctrace",APPEND_TEXT);
  166. #endif
  167.     /* Free allocated memory resources */
  168.     if(pp->freeargs){
  169.         argv = pp->parg1;
  170.         while(pp->iarg-- != 0)
  171.             free(*argv++);
  172.         free(pp->parg1);
  173.     }
  174.     free(pp->name);
  175.     free(pp->stack);
  176.     free(pp->outbuf);
  177.     free((char *)pp);
  178. }
  179. /* Terminate current process by sending a request to the killer process.
  180.  * Automatically called when a process function returns. Does not return.
  181.  */
  182. void
  183. killself()
  184. {
  185.     register struct mbuf *bp;
  186.  
  187.     if(Curproc != NULLPROC){
  188.         bp = pushdown(NULLBUF,sizeof(Curproc));
  189.         memcpy(bp->data,(char *)&Curproc,sizeof(Curproc));
  190.         enqueue(&Killq,bp);
  191.     }
  192.     /* "Wait for me; I will be merciful and quick." */
  193.     for(;;)
  194.         pwait(NULL);
  195. }
  196. /* Process used by processes that want to kill themselves */
  197. void
  198. killer(i,v1,v2)
  199. int i;
  200. void *v1;
  201. void *v2;
  202. {
  203.     struct proc *pp;
  204.     struct mbuf *bp;
  205.  
  206.     for(;;){
  207.         while(Killq == NULLBUF)
  208.             pwait(&Killq);
  209.         bp = dequeue(&Killq);
  210.         pullup(&bp,(char *)&pp,sizeof(pp));
  211.         free_p(bp);
  212.         if(pp != Curproc)    /* We're immortal */
  213.             killproc(pp);
  214.     }                        
  215. }
  216.  
  217. #ifdef    SUSPEND_PROC
  218. /* Inhibit a process from running */
  219. void
  220. suspend(pp)
  221. struct proc *pp;
  222. {
  223.     if(pp == NULLPROC)
  224.         return;
  225.     if(pp != Curproc)
  226.         delproc(pp);    /* Running process isn't on any list */
  227.     pp->state |= SUSPEND;
  228.     if(pp != Curproc)
  229.         addproc(pp);    /* pwait will do it for us */
  230.     else
  231.         pwait(NULL);
  232. }
  233. /* Restart suspended process */
  234. void
  235. resume(pp)
  236. struct proc *pp;
  237. {
  238.     if(pp == NULLPROC)
  239.         return;
  240.     delproc(pp);    /* Can't be Curproc! */
  241.     pp->state &= ~SUSPEND;
  242.     addproc(pp);
  243. }
  244. #endif    /* SUSPEND_PROC */
  245.  
  246. /* Wakeup waiting process, regardless of event it's waiting for. The process
  247.  * will see a return value of "val" from its pwait() call.
  248.  */
  249. void
  250. alert(pp,val)
  251. struct proc *pp;
  252. int val;
  253. {
  254.     if(pp == NULLPROC)
  255.         return;
  256. #ifdef    notdef
  257.     if((pp->state & WAITING) == 0)
  258.         return;
  259. #endif
  260. #ifdef    PROCTRACE
  261.     printf("alert(%lx,%u) [%s]\n",ptol(pp),val,pp->name);
  262.     fflush(stdout);
  263. #endif
  264.     if(pp != Curproc)
  265.         delproc(pp);
  266.     pp->state &= ~WAITING;
  267.     pp->retval = val;
  268.     pp->event = 0;
  269.     if(pp != Curproc)
  270.         addproc(pp);
  271. }
  272.  
  273. /* Post a wait on a specified event and give up the CPU until it happens. The
  274.  * null event is special: it means "I don't want to block on an event, but let
  275.  * somebody else run for a while". It can also mean that the present process
  276.  * is terminating; in this case the wait never returns.
  277.  *
  278.  * Pwait() returns 0 if the event was signaled; otherwise it returns the
  279.  * arg in an alert() call. Pwait must not be called from interrupt level.
  280.  *
  281.  * Note that pwait can run with interrupts enabled even though it examines
  282.  * a few global variables that can be modified by psignal at interrupt time.
  283.  * These *seem* safe.
  284.  */
  285. int
  286. pwait(event)
  287. void *event;
  288. {
  289.     register struct proc *oldproc;
  290.     int tmp;
  291.  
  292.     extern int WDTick;
  293.     extern int WDCurr;
  294.  
  295.     WDCurr = WDTick;        /* reset watchdog timer value */
  296.  
  297.     if(Curproc != NULLPROC){    /* If process isn't terminating */
  298.         if(Stkchk)
  299.             chkstk();
  300.  
  301.         if(event == NULL){
  302.             /* Special case; just give up the processor.
  303.              *
  304.              * Optimization: if nothing else is ready, just return.
  305.              */
  306.             if(Rdytab == NULLPROC){
  307.                 return 0;
  308.             }
  309.         } else {
  310.             /* Post a wait for the specified event */
  311.             Curproc->event = event;
  312.             Curproc->state = WAITING;
  313.         }
  314.         addproc(Curproc);
  315.     }
  316.     /* Look for a ready process and run it. If there are none,
  317.      * loop or halt until an interrupt makes something ready.
  318.      */
  319.     while(Rdytab == NULLPROC){
  320.         /* Give system back to upper-level multitasker, if any.
  321.          * Note that this function enables interrupts internally
  322.          * to prevent deadlock, but it restores our state
  323.          * before returning.
  324.          */
  325.         kbint();    /***/
  326.         giveup();
  327.     }
  328.     /* Remove first entry from ready list */
  329.     oldproc = Curproc;
  330.     Curproc = Rdytab;
  331.     delproc(Curproc);
  332.  
  333.     /* Now do the context switch.
  334.      * This technique was inspired by Rob, PE1CHL, and is a bit tricky.
  335.      *
  336.      * If the old process has gone away, simply load the new process's
  337.      * environment. Otherwise, save the current process's state. Then if
  338.      * this is still the old process, load the new environment. Since the
  339.      * new task will "think" it's returning from the setjmp() with a return
  340.      * value of 1, the comparison with 0 will bypass the longjmp(), which
  341.      * would otherwise cause an infinite loop.
  342.      */
  343. #ifdef    PROCTRACE
  344.     if(strcmp(oldproc->name,Curproc->name) != 0){
  345.           printf("pwait -> %s(%d)\n",Curproc->name,!!Curproc->i_state);
  346.         fflush(stdout);
  347.     }
  348. #endif
  349.     /* Note use of comma operator to save old interrupt state only if
  350.      * oldproc is non-null
  351.      */
  352.     if(oldproc == NULLPROC
  353.      || (oldproc->i_state = istate(), setjmp(oldproc->env) == 0)){
  354.         /* We're still running in the old task; load new task context.
  355.          * The interrupt state is restored here in case longjmp
  356.          * doesn't do it (e.g., systems other than Turbo-C).
  357.          */
  358.         restore(Curproc->i_state);
  359.         longjmp(Curproc->env,1);
  360.     }
  361.     /* At this point, we're running in the newly dispatched task */
  362.     tmp = Curproc->retval;
  363.     Curproc->retval = 0;
  364.  
  365.     /* Also restore the true interrupt state here, in case the longjmp
  366.      * DOES restore the interrupt state saved at the time of the setjmp().
  367.      * This is the case with Turbo-C's setjmp/longjmp.
  368.      */
  369.     restore(Curproc->i_state);
  370.     return tmp;
  371. }
  372.  
  373. /* Make ready the first 'n' processes waiting for a given event. The ready
  374.  * processes will see a return value of 0 from pwait().  Note that they don't
  375.  * actually get control until we explicitly give up the CPU ourselves through
  376.  * a pwait(). Psignal may be called from interrupt level. It returns the
  377.  * number of processes that were woken up.
  378.  */
  379. int
  380. psignal(event,n)
  381. void *event;    /* Event to signal */
  382. int n;        /* Max number of processes to wake up */
  383. {
  384.     register struct proc *pp;
  385.     struct proc *pnext;
  386.     int i_state;
  387.     unsigned int hashval;
  388.     int cnt = 0;
  389.  
  390.     if(Stkchk)
  391.         chkstk();
  392.  
  393.     if(event == NULL)
  394.         return 0;        /* Null events are invalid */
  395.  
  396.     /* n = 0 means "signal everybody waiting for this event" */
  397.     if(n == 0)
  398.         n = 65535;
  399.  
  400.     hashval = phash(event);
  401.     i_state = dirps();
  402.     for(pp = Waittab[hashval];n != 0 && pp != NULLPROC;pp = pnext){
  403.         pnext = pp->next;
  404.         if(pp->event == event){
  405. #ifdef    PROCTRACE
  406.             if(i_state){
  407.                 printf("psignal(%lx,%u) wake %lx [%s]\n",ptol(event),n,
  408.                  ptol(pp),pp->name);
  409.                 fflush(stdout);
  410.             }
  411. #endif
  412.             delproc(pp);
  413.             pp->state &= ~WAITING;
  414.             pp->retval = 0;
  415.             pp->event = NULL;
  416.             addproc(pp);
  417.             n--;
  418.             cnt++;
  419.         }
  420.     }
  421. #ifdef    SUSPEND_PROC
  422.     for(pp = Susptab;n != 0 && pp != NULLPROC;pp = pnext){
  423.         pnext = pp->next;
  424.         if(pp->event == event){
  425. #ifdef    PROCTRACE
  426.             if(i_state){
  427.                 printf("psignal(%lx,%u) wake %lx [%s]\n",ptol(event),n,
  428.                  ptol(pp),pp->name);
  429.                 fflush(stdout);
  430.             }
  431. #endif /* PROCTRACE */
  432.             delproc(pp);
  433.             pp->state &= ~WAITING;
  434.             pp->event = 0;
  435.             pp->retval = 0;
  436.             addproc(pp);
  437.             n--;
  438.             cnt++;
  439.         }
  440.     }
  441. #endif    /* SUSPEND_PROC */
  442.     restore(i_state);
  443.     return cnt;
  444. }
  445.  
  446. /* Rename a process */
  447. void
  448. chname(pp,newname)
  449. struct proc *pp;
  450. char *newname;
  451. {
  452.     free(pp->name);
  453.     pp->name = strdup(newname);
  454. }
  455. /* Remove a process entry from the appropriate table */
  456. static void
  457. delproc(entry)
  458. register struct proc *entry;    /* Pointer to entry */
  459. {
  460.     int i_state;
  461.  
  462.     if(entry == NULLPROC)
  463.         return;
  464.  
  465.     i_state = dirps();
  466.     if(entry->next != NULLPROC)
  467.         entry->next->prev = entry->prev;
  468.     if(entry->prev != NULLPROC){
  469.         entry->prev->next = entry->next;
  470.     } else {
  471.         switch(entry->state){
  472.         case READY:
  473.             Rdytab = entry->next;
  474.             break;
  475.         case WAITING:
  476.             Waittab[phash(entry->event)] = entry->next;
  477.             break;
  478. #ifdef    SUSPEND_PROC
  479.         case SUSPEND:
  480.         case SUSPEND|WAITING:
  481.             Susptab = entry->next;
  482.             break;
  483. #endif
  484.         }
  485.     }
  486.     restore(i_state);
  487. }
  488. /* Append proc entry to end of appropriate list */
  489. static void
  490. addproc(entry)
  491. register struct proc *entry;    /* Pointer to entry */
  492. {
  493.     register struct proc *pp;
  494.     struct proc **head;
  495.     int i_state;
  496.  
  497.     if(entry == NULLPROC)
  498.         return;
  499.  
  500.     switch(entry->state){
  501.     case READY:
  502.         head = &Rdytab;
  503.         break;
  504.     case WAITING:
  505.         head = &Waittab[phash(entry->event)];
  506.         break;
  507. #ifdef    SUSPEND_PROC
  508.     case SUSPEND:
  509.     case SUSPEND|WAITING:
  510.         head = &Susptab;
  511.         break;
  512. #endif
  513.     }
  514.     entry->next = NULLPROC;
  515.     i_state = dirps();
  516.     if(*head == NULLPROC){
  517.         /* Empty list, stick at beginning */
  518.         entry->prev = NULLPROC;
  519.         *head = entry;
  520.     } else {
  521.         /* Find last entry on list */
  522.         for(pp = *head;pp->next != NULLPROC;pp = pp->next)
  523.             ;
  524.         pp->next = entry;
  525.         entry->prev = pp;
  526.     }
  527.     restore(i_state);
  528. }
  529.